/*  This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2.1 of the License, or (at
    your option) any later version.
    For more details, see the GNU Lesser General Public License (www.fsf.org
    or the COPYING file somewhere in the package)
 */

#ifndef OSGI_SIGNAL_HH
#define OSGI_SIGNAL_HH

#include "OSGi.hh"

/*! @file OSGi/Signal.hh
    @brief Class OSGi::Signal header.
    @author @ref Guillaume_Terrissol
    @date 8th January 2010 - 26th May 2013
    @note This file is distributed under the LGPL license.
    Refer to the file COPYING (or http://www.fsf.org) for more information.
 */

#include <map>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>

#include "PImpl.hh"

namespace OSGi
{
//------------------------------------------------------------------------------
//                               Event Management
//------------------------------------------------------------------------------

    /// @cond DEVELOPMENT

    /*! @brief Generic signal.
        @version 0.7

        @tparam TArgs... Call parameters
     */
    template<class... TArgs>
    class Signal
    {
    public:
        //! @name Constructor & destructor
        //@{
                Signal();                                           //!< Constructor.
                ~Signal();                                          //!< Destructor.
        //@}
        template<class TRecv>
        void    connect(const std::string&       pName,
                        std::shared_ptr<TRecv>   pTarget,
                        void (TRecv::*           pSlot)(TArgs...)); //!< Connection.
        void    disconnect(const std::string&    pName,
                           std::shared_ptr<void> pTarget);          //!< Disconnection.
        void    operator()(TArgs... pArgs) const;                   //!< Emission.

    private:

        /*! @brief Base class for slots.
            @version 0.7
            @note Such a class was necessary as a base class for slots, because
            targets, held by slots, may be of any type
         */
        class SlotBase
        {
        public:
            //! @name Constructor & destructor
            //@{
                    SlotBase();                                     //!< Default constructor.
    virtual         ~SlotBase();                                    //!< Destructor.
            //@}
            //! @name Interface
            //@{
    inline  void    operator()(TArgs&&... pArgs) const;             //!< Slot call.
    inline  bool    expired() const;                                //!< Target validity.
    inline  void*   target();                                       //!< Slot target.
            //@{

        private:
            //! @name Implementation
            //@{
    virtual void    call(TArgs&&... pArgs) const = 0;               //!< Slot call.
    virtual bool    hasExpired() const = 0;                         //!< Target validity.
    virtual void*   getTarget() = 0;                                //!< Slot target.
            //@}
        };


        /*! @brief Single slot.
            @version 0.5
         */
        template<class TRecv>
        class Slot : public SlotBase
        {
        public:
            //! @name Constructor & destructor
            //@{
                    Slot(std::shared_ptr<TRecv> pTarget,
                         void (TRecv::*         pFunc)(TArgs...));  //!< Constructor.
    virtual         ~Slot();                                        //!< Destructor.
            //@}

        private:
            //! @name Implementation
            //@{
    virtual void    call(TArgs&&... pArgs) const override;          //!< Slot call.
    virtual bool    hasExpired() const override;                    //!< Target validity.
    virtual void*   getTarget() override;                           //!< Slot target.
            //@}
            std::weak_ptr<TRecv>    mTarget;                        //!< Slot target.
            void (TRecv::*          mFunc)(TArgs...);               //!< Callback (the slot itself).
        };

        using SlotBasePtr = std::shared_ptr<SlotBase>;              //!< Pointer to base slot.
        using Slots       = std::vector<SlotBasePtr>;               //!< Slot container.
        using NamedSlots  = std::map<std::string, Slots>;           //!< Slots sorted by name.
        NamedSlots  mSlots;                                         //!< Registered slots.
    };

    /// @endcond
}

#include "Signal.inl"

//------------------------------------------------------------------------------
//                                    Macros
//------------------------------------------------------------------------------

//! @brief Signal declaration macro
#define OSGI_DECL_SIGNAL(func,...)              \
private:                                        \
    OSGi::Signal<__VA_ARGS__>  m##func;         \
public:                                         \
    template<class... TArgs>                    \
    void func(TArgs... pArgs) const             \
    {                                           \
        m##func(pArgs...);                      \
    }                                           \
    OSGi::Signal<__VA_ARGS__>* signal##func()   \
    {                                           \
        return &m##func;                        \
    }

//! @brief Signal "marker" in connection
#define OSGI_SIGNAL(name)    \
    signal##name()

//! @brief Slot "marker" in connection
#define OSGI_SLOT(name)  \
    name

//! @brief Signal connection
#define OSGI_CONNECT(sender, signal, receiver, slot) \
    sender->signal->connect(#slot, receiver, &std::remove_reference<decltype(*receiver)>::type::slot);

//! @brief Signal disconnection
#define OSGI_DISCONNECT(sender, signal, receiver, slot)  \
    sender->signal->disconnect(#slot, receiver);

//! @brief Signal firing
#define OSGI_EMIT

#endif  // OSGI_SIGNAL_HH
